package com.example.java.base.concurrency.flowcontrol.countdownlatch;

import com.example.java.base.concurrency.flowcontrol.MyThreadFactory;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * CountDownLatchDemo2:并发流程控制之-递减计数器(锁存器),用法3 :一等多类和多等一混合使用,当然也可以拓展成多等多
 * 使用常见:多个运动员等待1个裁判的枪声,1个裁判等待多个运动员跑完比赛
 *
 * @author zhangxiaoxiang
 * @date 2021/04/20
 */
@Slf4j
public class CountDownLatchDemo2 {
    @SneakyThrows
    public static void main(String[] args) {
        //模拟一个裁判
        CountDownLatch begin = new CountDownLatch(1);
        //模拟5名运动员
        CountDownLatch end = new CountDownLatch(5);
        //创建线程池
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                5,
                5,
                60L, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(5),
                //自定义线程工厂
                new MyThreadFactory());
        //创建任务并放进线程池执行
        for (int i = 1; i <= 5; i++) {
            final int no = i;
            Runnable runnable = () -> {
                try {
                    Thread.sleep((long) (Math.random() * 1000));
                    log.info("{}号运动员,准备完毕,等待发令枪(这里可以耗时不一样,只是准备过程)", no);
                    //线程等待过程(这里多等一)
                    begin.await();
                    log.info("{}号运动员,开始起步了(这里几乎同一时刻执行的)", no);
                    //模拟跑得时间
                    Thread.sleep((long) (Math.random() * 10000));
                    log.info("{}号运动员,跑到了终点,end", no);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    //注意这里是一等多,等待5名运动员都结束,计数器减为0
                    end.countDown();
                    log.info("还有{}名运动员没有跑完全程", end.getCount());
                }
            };
            executor.execute(runnable);
        }
        //    裁判员检查场地和设备,到发令枪响的耗时
        Thread.sleep(3000);
        log.info("===============发令枪响起,比赛开始=====================");
        //多个运动员开始跑步
        begin.countDown();

        //一个裁判统计(阻塞者,眼睁睁地看着运动员,可能带着暴龙眼睛看得),等到 end.countDown()减为0,那么就结束了
        end.await();
        log.info("所有人到达终点,比赛结束");
        //关闭线程池(根据实际来)
        // executor.shutdown();
    }
}
